//
// Created by yanhai on 2020/2/21.
//

#pragma once

#include <tuple>
#include <regex>
#include <string>
#include <event2/http.h>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>

typedef std::map<std::string, std::string> options_type;

#define _OPTION_HEADER_START_MIME_TYPE R"(,\s*([^;,\s]+)([;,]\s*.+)?)"
#define _OPTION_HEADER_PIECE_RE R"(;\s*,?\s*("[^"\\]*(?:\\.[^"\\]*)*"|[^\s;,=*]+)(?:\*(\d+))?\s*(?:(?:\*\s*=\s*(?:([^\s]+?)'([^\s]*?)')?|=\s*)("[^"\\]*(?:\\.[^"\\]*)*"|[^;,]+)?)?\s*(.*)?)"

inline const char *evhttp_get_header_value(const struct evkeyvalq *headers, const char *key)
{
    auto __value = evhttp_find_header(headers, key);
    return __value != nullptr ? __value : "";
}

inline int get_content_length(const struct evkeyvalq *headers)
{
//    if headers.get("HTTP_TRANSFER_ENCODING", "") == "chunked":
//        return 0;
    auto _content_length = evhttp_find_header(headers, "Content-Length");
    if (_content_length)
        return boost::lexical_cast<int>(_content_length);
    else
        return 0;
}

inline void unquote_header_value(std::string &value, bool is_filename = false)
{
    if (!value.empty()) {
        if (value[0] == '"') {
            value = value.substr(1);
        }
        if (value[value.size() - 1] == '"') {
            value = value.substr(0, value.size() - 1);
        }

        if (!is_filename || boost::ends_with(value, "\\\\")) {
            boost::replace_all(value, "\\\\", "\\");
            boost::replace_all(value, "\\\"", "\"");
        }
    }
}

inline std::tuple<std::string, options_type> parse_options_header(const std::string &value)
{
    std::string mimetype;
    options_type options;
    static std::regex _option_header_start_mime_type(_OPTION_HEADER_START_MIME_TYPE);
    static std::regex _option_header_piece_re(_OPTION_HEADER_PIECE_RE);

    std::smatch result;
    std::string __value = "," + value;
    boost::replace_all(__value, "\n", ",");

    if (std::regex_match(__value, result, _option_header_start_mime_type)) {
        std::string continued_encoding;
        auto it = result.begin();
        mimetype = *++it;
        std::string rest = *++it;
        while (!rest.empty()) {
            if (!std::regex_match(rest, result, _option_header_piece_re)) {
                break;
            }

            auto it2 = result.begin();
            std::string option = *++it2;
            std::string count = *++it2;
            std::string encoding = *++it2;
            std::string language = *++it2;
            std::string option_value = *++it2;
            rest = *++it2;  // next

            unquote_header_value(option);
            unquote_header_value(encoding);

            if (count.empty()) {
                continued_encoding.clear();
            } else {
                if (encoding.empty())
                    encoding = continued_encoding;
                continued_encoding = encoding;
            }

            if (!option_value.empty()) {
                unquote_header_value(option_value, option == "filename");
                if (!continued_encoding.empty()) {
                    // todo option_value encoding ...
                    ;
                }
            }

            if (options.find(option) == options.end())
                options[option] = option_value;
        }
    }

    return std::make_tuple(mimetype, options);
}
